"""
声音池组件。
"""
import typing

from .base import BaseSession, BaseSessionType, BaseSessionParentType
from .framework import dispatch, session_received
from .utils import Int32Value, callback, FloatValue, to_any


@session_received
class SoundPool(BaseSession):
    """
    声音池，可以并发播放多个声音。
    使用本组件的步骤是先load预加载，然后再play播放。
    load过程是把声音文件下载缓存到设备端，这样后续播放的时候不会有卡顿现象。
    """
    name = 'SoundPool'

    @classmethod
    async def create(cls: typing.Type[BaseSessionType], parent: BaseSessionParentType, *args: typing.Any,
                     **kwargs: typing.Dict) -> BaseSessionType:
        """
        创建声音池。
        需提供的参数：
        @size: 池大小，允许最多同时播放多少声音。
        """
        if len(args) < 1:
            raise TypeError('Missing 1 required positional argument: `size`.')
        size = to_any(args[0], Int32Value)
        return await super().create(parent, size, *args[1:], **kwargs)

    async def on_receive(self, msg):
        """
        组件的事件转发。
        """
        await super().on_receive(msg)
        if msg.name == 'onLoad':
            dispatch(self.on_load(*msg.argv, sessionId=msg.session, futureId=msg.future))
        elif msg.name == 'onPlay':
            dispatch(self.on_play(*msg.argv, sessionId=msg.session, futureId=msg.future))

    @callback(bool)
    async def on_load(self, result, **kwargs):
        """
        当所有声音被加载后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        session_id = kwargs['sessionId'] if 'sessionId' in kwargs else None
        if not session_id:
            return
        future_id = kwargs['futureId'] if 'futureId' in kwargs else 0
        self.set_action_result(session_id, future_id, result)

    async def load(self, base_url: str, *pathname: str):
        """
        预加载网络声音资源，会把数据都缓存到设备本地，从而加速播放并节省带宽资源。
        缓存有效时间是一天，如果某个音效已经被缓存且在有效期内，则不会继续请求下载，如果已经超出有效时间则重新下载。
        如果想更换音效则需要清除设备的应用缓存，或者给音效更换个名称，否则更换音效资源将在一天后才能生效。
        @base_url: 基本URL(音效根目录），例如`https://example.com`
        @pathname: 声音的完整路径（base_url + pathname组成完整的url），可以填写多个。
        其中pathname在整个声音池中具有唯一性，如果同时加载了多个相同pathname的资源（即使baseUrl不同）则最后加载成功的将覆盖已有的。
        调用示例：
        names = [
          '/test.wav',
          '/test2.ogg',
          '/foot/1.ogg'
        ]
        await pool.load('https://example.com', *names)
        """
        return await self.send_action_until_return('load', to_any(base_url), *[to_any(i) for i in pathname])

    async def stop(self, play_id: int):
        """
        停止播放。
        @play_id: 通过play()方法生成的播放ID
        """
        return await self.send_action_until_return('stop', to_any(play_id, Int32Value))

    async def play(self, pathname: str, left_volume: float = 1.0, right_volume: float = 1.0, rate: float = 1.0,
                   loop: int = 0) -> int:
        """
        开始播放并返回播放ID。
        @pathname: 请先使用load预加载，这里的pathname是传给load函数的资源名称。
        @left_volume: 左边音量（取值0到1）
        @right_volume: 右边音量（取值0到1）
        @rate: 播放音调（取值0.5到2.0，1.0正常）
        @loop: 循环次数，-1为无限循环，0表示不循环，x(x>0)表示循环x次（播放1次+循环x次，总播放x+1次）
        """
        return await self.send_action_until_return('play', to_any(pathname), to_any(left_volume, FloatValue),
                                                   to_any(right_volume, FloatValue), to_any(rate, FloatValue),
                                                   to_any(loop, Int32Value))

    @callback(int)
    async def on_play(self, play_id, **kwargs):
        """
        当音效播放开始，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        session_id = kwargs['sessionId'] if 'sessionId' in kwargs else None
        if not session_id:
            return
        future_id = kwargs['futureId'] if 'futureId' in kwargs else 0
        self.set_action_result(session_id, future_id, play_id)
